package cn.demoncat.util.sms.aliyun;

import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.lang.LogUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.MapSs;
import cn.demoncat.util.lang.entity.Result;
import cn.demoncat.util.lang.entity.SmsData;
import cn.demoncat.util.lang.msg.SmsLogMsg;
import cn.demoncat.util.lang.msg.SmsMsg;
import cn.demoncat.util.sms.SmsInterface;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;

/**
 * 阿里云短信服务
 * 
 * @author 延晓磊
 *
 * @since 2019年1月9日
 */
@Component("smsAliyunUtil")
public final class SmsAliyunUtil implements SmsInterface{

	// 产品名称:云通信短信API产品
	private static final String PRODUCT = "Dysmsapi";
	// 产品域名
	private static final String DOMAIN = "dysmsapi.aliyuncs.com";
	// 请求超时设置
	private static final String CONNECTION_TIMEOUT = "20000";
	private static final String READ_TIMEOUT = "20000";
	// 状态码
	private static final String CODE_LIMIT = "isv.BUSINESS_LIMIT_CONTROL";
	
	/**
	 * 占位符变量名前缀
	 * 
	 * 1、因为阿里云短信模板的占位符变量名约束太多，防止冲突，统一使用${n1} ${n2}的格式
	 * 2、为了与其它短信平台通用模板内容，模板占位符统一使用  ${n1} ${n2} ...，即使用'n1','n2','……'的序号作为占位符KEY
	 */
	private static final String VAR_PRE = "n";
	
	private static final Logger LOG = LogUtil.get(SmsAliyunUtil.class);
	
	// 阿里云配置
	@Resource
	private SmsAliyunProperties smsAliyunProperties;
	// 阿里云手机号数量限制
	public static final int MAX_PHONES = 1000;
 
	/**
	 * 发送短信
	 * 
	 * 注：如果手机号超过1000条，会批量发送，但只返回最后一次发送的响应结果，这样就与服务端的流水号会不一致
	 * 
	 * @param data		请求数据（引用传值）
	 * 
	 * @return Result	处理后的结果[500请求失败,400发送出错,200发送成功]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年1月9日
	 */
	@Override
	public Result send(SmsData data) {
		LOG.info(SmsLogMsg.SEND_SMS, data.getAppName(), data.getTemplateId(), data.getPhone());
		
		// 清空回执编号
		data.setBizId(StringConstant.EMPTY);
		
		// 短信参数 = {"n1":"延晓磊", "n2": "1234"}
		String param = getParam(data.getParam());
		
		// 设置请求超时
		System.setProperty("sun.net.client.defaultConnectTimeout", CONNECTION_TIMEOUT);
		System.setProperty("sun.net.client.defaultReadTimeout", READ_TIMEOUT);
		
		// 初始化AcsClient
		IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsAliyunProperties.getAccessKeyId(), smsAliyunProperties.getAccessKeySecret());
		try {
			DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
		} catch (ClientException e) {
			// 客户端问题
			LOG.error(SmsLogMsg.SEND_SMS_ERROR_BEFORE, data.getAppName(), data.getTemplateId(), data.getPhone(), e);
			data.setCode(e.getErrCode());
			data.setMessage(e.getErrMsg());
			return Result.FAIL;
		}
		IAcsClient acsClient = new DefaultAcsClient(profile);
		
		// 组装请求对象
		SendSmsRequest request = new SendSmsRequest();
		// 必填:短信签名
		request.setSignName(data.getAppName());
		// 必填:短信模板
		request.setTemplateCode(data.getTemplate());
		// 可选:短信参数
		request.setTemplateParam(param);
		// 必填:待发送手机号
		request.setPhoneNumbers(data.getPhone());
		
		// 发送消息
		if (data.getPhones().size() < MAX_PHONES) {
			// 单次
			return send(data, param, request, acsClient);
		}else {
			// 多次
			int size = MAX_PHONES / 2;
			int times = data.getPhones().size() % size == 0 ? data.getPhones().size()/size : data.getPhones().size()/size + 1;
			ArrayList<String> phones = new ArrayList<>(data.getPhones());
			String phone;
			Result result = null;
			for (int i = 0;  i < times; i++) {
				// 分批手机号
				if (i == times -1) {
					phone = StringUtil.join(phones.subList(i*size, phones.size()));
				}else {
					phone = StringUtil.join(phones.subList(i*size, (i+1)*size));
				}
				request.setPhoneNumbers(phone);
				// 发送
				result = send(data, param, request, acsClient);
				if (!Result.isSuccess(result)) {
					// 出错停止
					return result;
				}
			}
			return result;
		}
	}

	/**
	 * 转换为阿里云短信参数
	 * 
	 * @param param [a,b]
	 * @return {n1:a,n2:b}
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年3月28日
	 */
	private static String getParam(String[] param) {
		if (param == null || param.length == 0) {
			return StringConstant.EMPTY;
		}
		MapSs map = new MapSs();
		for (int i = 0; i < param.length; i++) {
			map.put(VAR_PRE + (i+1), param[i]);
		}
		return JsonUtil.toStr(map);
	}
	
	/**
	 * 发送短信
	 * 
	 * @param data
	 * @param param
	 * @param request
	 * @param acsClient
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年7月16日
	 */
	private static Result send(SmsData data, String param, SendSmsRequest request, IAcsClient acsClient) {
		SendSmsResponse response;
		try {
			// 发送短信
			response = acsClient.getAcsResponse(request);
		} catch (ClientException e) {
			LOG.error(SmsLogMsg.SEND_SMS_ERROR_BEFORE, data.getAppName(), data.getTemplateId(), data.getPhone(), e);
			data.setCode(e.getErrCode());
			data.setMessage(e.getErrMsg());
			return Result.FAIL;
		}
		// 结果校验
		data.setCode(response.getCode());
		data.setMessage(response.getMessage());
		data.setBizId(response.getBizId());
		if (StringConstant.OK.equals(response.getCode())) {
			return Result.SUCCESS;
		}else {
			LOG.error(SmsLogMsg.SEND_SMS_ERROR_AFTER, data.getAppName(), data.getTemplateId(), data.getPhone(), param, response);
			if (CODE_LIMIT.equals(response.getCode())) {
				// 业务限流
				return Result.error(SmsMsg.SMS_SEND_ERROR_OFTEN);
			}else {
				// 系统原因
				return Result.error(SmsMsg.SMS_SEND_ERROR);
			}
		}
	}
	
}
